﻿using System;
using System.Linq;
using System.Reflection;
using System.Linq.Expressions;
using System.Data.Linq.Mapping;
using System.Collections.Generic;

using LeanEngine.Entity;
using LeanEngine.Data.Linq.Language;

namespace LeanEngine.Data.Linq.Extension
{
    internal static class CustomExtensions
    {
        #region Expression Extensions

        internal static bool IsNullConstant(this ConstantExpression constant)
        {
            return (constant != null && constant.Value == null);
        }

        /// <summary>
        /// Is the expression comparing a value with NULL such as:
        /// c.CustomerName = NULL in which case it has to be
        /// reworked to look like (c.CustomerName is NULL)
        /// </summary>
        /// <returns></returns>
        internal static bool IsComparingWithNull(this BinaryExpression binary)
        {
            if (binary.NodeType == ExpressionType.Equal || binary.NodeType == ExpressionType.NotEqual)
            {
                return (binary.Left as ConstantExpression).IsNullConstant() ||
                    (binary.Right as ConstantExpression).IsNullConstant();
            }
            return false;
        }

        #endregion

        #region List<> Extensions

        internal static TableToken Where(this List<TableToken> list, Type tableType)
        {
            var table = list.Where(t => t.Metadata.EntityType == tableType).FirstOrDefault();
            if (table == null)
            {
                var metadata = MetaTableCache.GetItem(tableType);
                table = new TableToken(metadata);

                list.Add(table);
            }

            return table;
        }

        #endregion

        #region Type Extensions

        internal static Type GetElementType(this Type seqType)
        {
            Type type = FindIEnumerable(seqType);
            if (type == null)
            {
                return seqType;
            }
            return type.GetGenericArguments()[0];
        }

        private static Type FindIEnumerable(Type seqType)
        {
            if ((seqType != null) && (seqType != typeof(string)))
            {
                if (seqType.IsArray)
                {
                    return typeof(IEnumerable<>).MakeGenericType(new[] { seqType.GetElementType() });
                }
                if (seqType.IsGenericType)
                {
                    foreach (Type type in seqType.GetGenericArguments())
                    {
                        Type type2 = typeof(IEnumerable<>).MakeGenericType(new[] { type });
                        if (type2.IsAssignableFrom(seqType))
                        {
                            return type2;
                        }
                    }
                }
                Type[] interfaces = seqType.GetInterfaces();
                if ((interfaces != null) && (interfaces.Length > 0))
                {
                    foreach (Type type3 in interfaces)
                    {
                        Type type4 = FindIEnumerable(type3);
                        if (type4 != null)
                        {
                            return type4;
                        }
                    }
                }
                if ((seqType.BaseType != null) && (seqType.BaseType != typeof(object)))
                {
                    return FindIEnumerable(seqType.BaseType);
                }
            }
            return null;
        }

        #endregion
    }
}
